package cn.csc.framework.mybatis.core.query;

import cn.csc.framework.common.util.collection.ArrayUtils;
import cn.hutool.core.util.ArrayUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 *  拓展 MyBatis Plus QueryWrapper 类，主要增加如下功能：
 *
 *  1. 拼接条件的方法，增加 xxxIfPresent 方法，用于判断值不存在的时候，不要拼接到条件中。
 *
 *  @param <T> 数据类型
 * @author liuchuang
 * @version 1.0
 * @date 2024/1/25 17:43
 * @modify
 */
@Slf4j
public class LambdaUpdateWrapperX<T> extends LambdaUpdateWrapper<T> {


    /**
     * SQL 更新字段内容，例如：name='1', age=2
     */
    private final List<String> sqlSet;

    public LambdaUpdateWrapperX() {
        // 如果无参构造函数，请注意实体 NULL 情况 SET 必须有否则 SQL 异常
        this((T) null);
    }

    public LambdaUpdateWrapperX(T entity) {
        super.setEntity(entity);
        super.initNeed();
        this.sqlSet = new ArrayList<>();
    }

    /**
     * 指定列自增
     * @param column           列引用
     * @param value             增长值
     */
    public LambdaUpdateWrapperX<T> increField(SFunction<T, ?> column, Object value) {
        String columnsToString = super.columnToString(column);
        ColumnCache columnCache = getColumnCache(column);

        String format = String.format("%s =  %s + %s", columnsToString,columnsToString, formatParam(columnCache.getMapping(), value));
        setSql(format);

        return this;
    }

    /**
     * 指定列自减
     * @param column           列引用
     * @param value             减少值
     */
    public LambdaUpdateWrapperX<T> decreField(SFunction<T, ?> column, Object value) {
        String columnsToString = super.columnToString(column);
        ColumnCache columnCache = getColumnCache(column);

        String format = String.format("%s =  %s - %s", columnsToString,columnsToString, formatParam(columnCache.getMapping(), value));
        setSql(format);

        return this;
    }


    public LambdaUpdateWrapper<T> setIfPresent(SFunction<T, ?> column, String val) {
        if (StringUtils.hasText(val)) {
            return maybeDo(true, () -> {
                String sql = formatParam(getColumnCache(column).getMapping(), val);
                sqlSet.add(columnToString(column) + Constants.EQUALS + sql);
            });
        }
        return this;
    }

    public LambdaUpdateWrapperX<T> likeIfPresent(SFunction<T, ?> column, String val) {
        if (StringUtils.hasText(val)) {
            return (LambdaUpdateWrapperX<T>) super.like(column, val);
        }
        return this;
    }

    public LambdaUpdateWrapperX<T> inIfPresent(SFunction<T, ?> column, Collection<?> values) {
        if (!CollectionUtils.isEmpty(values)) {
            return (LambdaUpdateWrapperX<T>) super.in(column, values);
        }
        return this;
    }

    public LambdaUpdateWrapperX<T> inIfPresent(SFunction<T, ?> column, Object... values) {
        if (!ArrayUtil.isEmpty(values)) {
            return (LambdaUpdateWrapperX<T>) super.in(column, values);
        }
        return this;
    }

    public LambdaUpdateWrapperX<T> eqIfPresent(SFunction<T, ?> column, Object val) {
        if (val != null) {
            return (LambdaUpdateWrapperX<T>) super.eq(column, val);
        }
        return this;
    }

    public LambdaUpdateWrapperX<T> neIfPresent(SFunction<T, ?> column, Object val) {
        if (val != null) {
            return (LambdaUpdateWrapperX<T>) super.ne(column, val);
        }
        return this;
    }

    public LambdaUpdateWrapperX<T> gtIfPresent(SFunction<T, ?> column, Object val) {
        if (val != null) {
            return (LambdaUpdateWrapperX<T>) super.gt(column, val);
        }
        return this;
    }

    public LambdaUpdateWrapperX<T> geIfPresent(SFunction<T, ?> column, Object val) {
        if (val != null) {
            return (LambdaUpdateWrapperX<T>) super.ge(column, val);
        }
        return this;
    }

    public LambdaUpdateWrapperX<T> ltIfPresent(SFunction<T, ?> column, Object val) {
        if (val != null) {
            return (LambdaUpdateWrapperX<T>) super.lt(column, val);
        }
        return this;
    }

    public LambdaUpdateWrapperX<T> leIfPresent(SFunction<T, ?> column, Object val) {
        if (val != null) {
            return (LambdaUpdateWrapperX<T>) super.le(column, val);
        }
        return this;
    }

    public LambdaUpdateWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object val1, Object val2) {
        if (val1 != null && val2 != null) {
            return (LambdaUpdateWrapperX<T>) super.between(column, val1, val2);
        }
        if (val1 != null) {
            return (LambdaUpdateWrapperX<T>) ge(column, val1);
        }
        if (val2 != null) {
            return (LambdaUpdateWrapperX<T>) le(column, val2);
        }
        return this;
    }

    public LambdaUpdateWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object[] values) {
        Object val1 = ArrayUtils.get(values, 0);
        Object val2 = ArrayUtils.get(values, 1);
        return betweenIfPresent(column, val1, val2);
    }

    // ========== 重写父类方法，方便链式调用 ==========

    @Override
    public LambdaUpdateWrapperX<T> eq(boolean condition, SFunction<T, ?> column, Object val) {
        super.eq(condition, column, val);
        return this;
    }

    @Override
    public LambdaUpdateWrapperX<T> eq(SFunction<T, ?> column, Object val) {
        super.eq(column, val);
        return this;
    }

    @Override
    public LambdaUpdateWrapperX<T> orderByDesc(SFunction<T, ?> column) {
        super.orderByDesc(true, column);
        return this;
    }

    @Override
    public LambdaUpdateWrapperX<T> last(String lastSql) {
        super.last(lastSql);
        return this;
    }

    @Override
    public LambdaUpdateWrapperX<T> in(SFunction<T, ?> column, Collection<?> coll) {
        super.in(column, coll);
        return this;
    }


}
